package com.template.common.exception.handler;

import com.template.common.exception.PermissionException;
import com.template.common.exception.CommonException;
import com.template.common.exception.IdentityException;
import com.template.common.lang.R;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MaxUploadSizeExceededException;


@Slf4j
@ResponseStatus(HttpStatus.OK)
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(CommonException.class)
    public R<?> handler(CommonException e) {
        return R.fail(e.getMessage());
    }

    @ExceptionHandler(PermissionException.class)
    public R<?> handler(PermissionException e) {
        log.warn("权限不足: -----------{}", e.getMessage());
        return e.getMessage() == null ? R.noPerms() : R.noPerms(e.getMessage());
    }

    @ExceptionHandler(IdentityException.class)
    public R<?> handler(IdentityException e) {
        log.warn("token异常: -----------{}", e.getMessage());
        // 需要重新登录
        return R.login(e.getMessage());
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public R<?> handler(DuplicateKeyException e) {
        String message = e.getMessage();
        log.warn("数据库异常: -----------");
        e.printStackTrace();

        String uniqueKeyword = "Duplicate entry ";
        int constraintPrefixIndex;
        if ((constraintPrefixIndex = message.indexOf(uniqueKeyword)) >= 0) {
            // 存在唯一性约束异常
            int keyStartIndex = message.indexOf('\'', constraintPrefixIndex + uniqueKeyword.length());
            int keyEndIndex = message.indexOf('\'', keyStartIndex + 1);
            String key = message.substring(keyStartIndex, keyEndIndex + 1);
            return R.fail(key + " 已存在");
        }
        return R.fail("数据异常");
    }

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public R<?> handle(MaxUploadSizeExceededException e) {
        log.warn("触发文件大小限制异常");
        String msg;
        if (e.getCause().getCause() instanceof FileSizeLimitExceededException) {
            msg = String.format("上传文件过大 [ 单文件大小不得超过 %sMB ]", "wenjiantaida");
        } else if (e.getCause().getCause() instanceof SizeLimitExceededException) {
            msg = String.format("上传文件过大 [ 上传总文件大小不得超过 %sMB ]", "wenjiantaida");
        } else {
            msg = "上传文件失败";
        }
        return R.fail(msg);
    }

    @ExceptionHandler(IllegalAccessException.class)
    public R<?> handler(IllegalAccessException e) {
        log.error("Assert异常: -----------{}", e.getMessage());
        return R.fail(e.getMessage());
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public R<?> handler(IllegalArgumentException e) {
        log.error("Assert异常: -----------{}", e.getMessage());
        return R.fail(e.getMessage());
    }

    /**
     * 对方法参数校验异常处理方法(仅对于表单提交有效，对于以json格式提交将会失效)
     * 如果是表单类型的提交，则spring会采用表单数据的处理类进行处理（进行参数校验错误时会抛出BindException异常）
     */
    @ExceptionHandler(BindException.class)
    public R<?> handler(BindException exception) {
        return bindingResult(exception);
    }

    /**
     * 对方法参数校验异常处理方法(前端提交的方式为json格式出现异常时会被该异常类处理)
     * json格式提交时，spring会采用json数据的数据转换器进行处理（进行参数校验时错误是抛出MethodArgumentNotValidException异常）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<?> handler(MethodArgumentNotValidException exception) {
        return bindingResult(exception);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public R<?> handler(ConstraintViolationException exception) {
        return R.fail(exception.getMessage());
    }

    private R<?> bindingResult(BindException exception) {
        BindingResult result = exception.getBindingResult();
        FieldError error = result.getFieldError();
        return error == null ? R.fail("参数异常") : R.fail(error.getDefaultMessage());
    }


    @ExceptionHandler(Exception.class)
    public R<?> handler(Exception e) {
        e.printStackTrace();
        return R.fail("系统异常");
    }
}
